﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Windows.Controls;
using System.Windows.Ink;
using System.Windows.Media;
using BitsOfStuff.MyActions;
using BitsOfStuff.Support;

namespace BitsOfStuff
{
    /// <summary>
    /// Class that provide conversion between raw strokes and sekvencions
    /// </summary>
    public class MyShapeRecognize
    {
        private readonly BasicCurvedAnalysis _basicCurvedAnalysis;
        private MyCharRecognizer _myCharRecognizer;

        private bool _recordMode;
        private bool _corectionMode;
        private bool _definitive;

        /// <summary>
        ///
        /// </summary>
        public bool deleteMode { get; set; }

        private string _curentUserProfilePath;
        private Stroke _lastStroke;
        private readonly StrokeCollection _strokesBackup;

        //PrivateGraficReference
        private InkCanvas _inkCanvas;

        public InkCanvas getInkCanvas()
        {
            return _inkCanvas;
        }

        //Private variable
        private List<int> _lastSequence;
        private readonly List<List<int>> _allSequences;
        /*
                private int _actualStrokesInShape;
        */
        private MyShape _actualmyShape;
        private MyPoint[] _actualMypoints;
        private List<bool[]> _lastSequenceBitmaps;

        /*
                private int _countStrokesAnalyzed;
        */

        private bool _shadowEvent;

        /// <summary>
        ///
        /// </summary>
        /// <param name="basicCurved"></param>
        /// <param name="inkCanvas"></param>
        public MyShapeRecognize(BasicCurvedAnalysis basicCurved,
                                  InkCanvas inkCanvas)
        {
            MyParameters.inicialize();
            _basicCurvedAnalysis = basicCurved;
            _inkCanvas = inkCanvas;
            _allSequences = new List<List<int>>();
            _shadowEvent = true;
            _recordMode = false;
            _corectionMode = false;
            _strokesBackup = new StrokeCollection();

            deleteMode = false;
        }

        public string getUserProfileFilePath()
        {
            return _curentUserProfilePath;
        }

        public void setDefinitive()
        {
            if (getLastActionMade())
            {
                _definitive = true;
                _strokesBackup.Clear();
            }
        }

        public void setUndefinitive()
        {
            _definitive = false;
        }

        public void switchRecordMode()
        {
            _recordMode = !_recordMode;
        }

        public void switchCorectionMode()
        {
            _corectionMode = !_corectionMode;

            if (_corectionMode)
            {
                corectionModeStart();
            }
            else
            {
                _recordMode = false;
            }
        }

        private void corectionModeStart()
        {
            _recordMode = true;
            clear();
            foreach (Stroke stroke in _strokesBackup)
            {
                analyze(stroke);
            }
        }

        public bool getRecorMode()
        {
            return _recordMode;
        }

        public bool getCorectionMode()
        {
            return _corectionMode;
        }

        public void assignInkCanvas(InkCanvas inkCanvas)
        {
            _inkCanvas = inkCanvas;
            _myCharRecognizer.assignNewResorces(inkCanvas);
        }

        public void saveUserProfile(string filePath)
        {
            _myCharRecognizer.saveUserProfile(filePath);
        }

        public void loadUserProfile(string filePath)
        {
            _myCharRecognizer = new MyCharRecognizer(SupportFunctions.getFileName(filePath), this);
            try
            {
                _myCharRecognizer.loadUserProfile(filePath);
                _curentUserProfilePath = filePath;
            }
            catch (Exception)
            {
                //TODO
            }
        }

        public void creatUserProfile(string name)
        {
            string filename = SupportFunctions.getFileName(name);
            _myCharRecognizer = new MyCharRecognizer(filename, this);
        }

        private List<bool[]> convertStrokeToBitmaps(Stroke stroke)
        {
            var resetEvents = new ManualResetEvent[5];
            var complete = new CountdownEvent(1);
            _actualMypoints = SupportFunctions.makeMyPointsToArray(stroke);
            if (ShortShapeRecognition.isShortShape(_actualMypoints))
            {
                return new List<bool[]> { ShortShapeRecognition.analyze(_actualMypoints) };
            }

            complete.TryAddCount();

            complete.TryAddCount();

            var cornerAnalysedBitMap = BasicCornerAnalysis.analyseThread(_actualMypoints);

            BasicCurvedAnalysis basicCurvedAnalysis = _basicCurvedAnalysis;
            bool[] extremePoints = basicCurvedAnalysis.selectExtremePoints(cornerAnalysedBitMap, _actualMypoints);

            bool[] dpAnalysisPoints = DouglasPeuckerReduction.douglasPeuckerReduction(_actualMypoints,
                                                                                      MyParameters.pdtolerance,
                                                                                    cornerAnalysedBitMap);

            //bool[] dpAnalysisPointsThread = DouglasPeuckerReduction.douglasPeuckerReductionThread(_actualMypoints,
            //                                                                                   MyParameters.
            //                                                                                     pdtolerance,
            //                                                                               cornerAnalysedBitMap);

            var bitMaps = SupportFunctions.finalSmoothing(_actualMypoints, cornerAnalysedBitMap, extremePoints, dpAnalysisPoints);

            //SupportDrawnFunctions.drawPoints(_inkCanvas, _actualMypoints, bitMaps[3], Colors.DarkOrange);
            SupportDrawnFunctions.drawPoints(_inkCanvas, _actualMypoints, bitMaps[2], Colors.DarkOrange);
            SupportDrawnFunctions.drawPoints(_inkCanvas, _actualMypoints, bitMaps[1], Colors.Chartreuse);
            SupportDrawnFunctions.drawPoints(_inkCanvas, _actualMypoints, bitMaps[0], Colors.Blue);

            return bitMaps;
        }

        private List<int> convertBitmapsToSequence(List<bool[]> bitMaps)
        {
            _actualmyShape = new MyShape(SupportFunctions.makeSelectedPoint(bitMaps, _actualMypoints));
            var result = convertBitmapToSequence(_actualmyShape);
            Debug.WriteLine(String.Format("Sequence : {0}", SupportFunctions.convertFromSequenceToString(result)));
            return result;
        }

        private List<int> convertBitmapToSequence(bool[] bitMap)
        {
            _actualmyShape = new MyShape(SupportFunctions.makeSelectedPoint(bitMap, _actualMypoints));
            return convertBitmapToSequence(_actualmyShape);
        }

        private List<int> convertBitmapToSequence(MyShape myShape)
        {
            //_actualmyShape = new MyShape(SupportFunctions.makeSelectedPoint(bitMaps, _actualMypoints));
            List<int> charSequence = new List<int>();
            myShape.assignSegments();
            myShape.mergeSameSegmentsLine();
            myShape.drawn(_inkCanvas);
            charSequence.AddRange(myShape.getSequence());
            charSequence.Add(-1);
            return charSequence;
        }

        private List<int> converBitMapToSequenceNonStreight(List<bool[]> bitMaps)
        {
            _actualmyShape.setUserDiverse(MyParameters.shapeShadowAngle);
            _actualmyShape.clearSequence();
            return convertBitmapToSequence(_actualmyShape);
        }

        public bool getLastActionMade()
        {
            return _myCharRecognizer.getLastActionMade();
        }

        public void analyze(Stroke stroke)
        {
            FootstepRecognition.addStroke(_lastStroke);
            _lastSequenceBitmaps = convertStrokeToBitmaps(stroke);
            _lastSequence = convertBitmapsToSequence(_lastSequenceBitmaps);
            _allSequences.Add(new List<int>(_lastSequence));
            var posibleO = new List<MyAction>();
            if (ActionArbitr.isItRaw(stroke, out posibleO))
            {
                _myCharRecognizer.Event(posibleO.FirstOrDefault());
                return;
            }
            if (!_recordMode)
                _myCharRecognizer.findAction(_lastSequence);
            _lastSequence.Clear();
        }

        public void analyze(StrokeCollection strokes)
        {
            Stroke stroke = strokes.LastOrDefault();
            _lastStroke = stroke;
            _strokesBackup.Add(stroke);

            analyze(_lastStroke);
        }

        public void addActionToShape(MyAction myAction)
        {
            List<int> newSequence = new List<int>();
            foreach (var i in _allSequences)
            {
                newSequence.AddRange(i);
            }
            _myCharRecognizer.addSequenceToProfile(newSequence, myAction);
        }

        public void clear()
        {
            _allSequences.Clear();
            _myCharRecognizer.deleteAllCurentAction();
            FootstepRecognition.reInitial();
        }

        public bool isInDecisionState()
        {
            return _myCharRecognizer.isInDecisionState();
        }

        public void tryChangeDecisionState(MyPoint myPoint)
        {
            _myCharRecognizer.tryEndDecisionState(myPoint);
        }

        public void getLastStrokeStarPoints(ref MyPoint start, ref MyPoint end)
        {
            start = MyPoint.makePoint(_lastStroke.StylusPoints[0]);
            end = MyPoint.makePoint(_lastStroke.StylusPoints.LastOrDefault());
        }

        public List<MySelectedPoint> getSelectionPoint()
        {
            return _actualmyShape.getSelectedPoint();
        }

        public void Event(MyCharNotFoundAction action)
        {
            if (_shadowEvent)
            {
                _shadowEvent = false;
                SupportDrawnFunctions.switchTimerSig();
                var divirsedLastSequence = converBitMapToSequenceNonStreight(_lastSequenceBitmaps);
                if (!SupportFunctions.compareList(_lastSequence, divirsedLastSequence))
                    _myCharRecognizer.findSequenceVectorShadow(divirsedLastSequence);
            }
            _shadowEvent = true;
        }

        public Stroke getLastStroke()
        {
            return _lastStroke;
        }

        public MyPoint getLastStrokeFirstPoint()
        {
            return _actualmyShape.getShapeStartPoint();
        }

        public double getLastShapeSize()
        {
            return _actualmyShape.getShapeSize();
        }

        public List<int> getLastSequence()
        {
            return _lastSequence;
        }

        internal void forceAddAction(MyShowAction myShowAction)
        {
            _myCharRecognizer.Event(myShowAction);
        }

        public void addOrRemoveAction(MyShowAction action)
        {
            if (deleteMode)
            {
                removeAction(action);
                deleteMode = false;
            }
            else
            {
                addActionToShape(action);
            }
        }

        private void removeAction(MyShowAction action)
        {
            _myCharRecognizer.removeAction(action);
        }

        internal void forceAddActionNoConfifance() { }
    }
}